package cn.edu.hit.file_runner;

import cn.edu.hit.core.Document;
import cn.edu.hit.core.conf.ConfigureLoader;
import cn.edu.hit.kg.data.GraphData;
import cn.edu.hit.kg.entity.*;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.cli.*;

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
//        args = new String[]{"-f", "D:\\Yu-hsin Wang\\Desktop\\读文件的推理机\\graph.json", "-o", "D:\\Yu-hsin Wang\\Desktop\\读文件的推理机\\output", "-s", "D:\\Yu-hsin Wang\\Desktop\\读文件的推理机\\signals.json"};
//        args = new String[]{"-n", "bolt://localhost", "-o", "D:\\Yu-hsin Wang\\Desktop\\读文件的推理机\\output", "-s", "D:\\Yu-hsin Wang\\Desktop\\读文件的推理机\\signals.json"};
        Options options = new Options();

        OptionGroup group1 = new OptionGroup();
        group1.addOption(new Option("h","help",false,"打印此帮助信息"));

        Option optionNeo4j = new Option("n", "neo4j", true, "从Neo4J加载知识库,参数是neo4j bolt路径,可通过-u与-p指定用户名和密码");
        group1.addOption(optionNeo4j);

        group1.addOption(new Option("f","file",true,"从JSON文件加载知识库，参数是知识库文件路径"));
        group1.setRequired(true);
        options.addOptionGroup(group1);

        Option optTimes = new Option("t","times",true,"执行次数");
        optTimes.setRequired(false);
        options.addOption(optTimes);

        Option optUser = new Option("u","user",true,"neo4j用户名,默认为neo4j");
        optUser.setRequired(false);
        options.addOption(optUser);

        Option optPassword = new Option("p","password",true,"neo4j密码,默认为123456");
        optPassword.setRequired(false);
        options.addOption(optPassword);

        Option optOutput = new Option("o","output",true,"输出路径(文件夹)");
        optOutput.setRequired(true);
        options.addOption(optOutput);

        Option optSignal = new Option("s","signal",true,"特征文件路径");
        optSignal.setRequired(true);
        options.addOption(optSignal);

        OptionGroup optExtOptions = new OptionGroup();
        optExtOptions.setRequired(false);

        Option optUseFeature = new Option("x","use-feature",false,"直接使用特征是否成立作为输入");
        optUseFeature.setRequired(false);
        optExtOptions.addOption(optUseFeature);

        Option optOldMethod = new Option("y","use-old-method",false,"使用旧推理策略");
        optOldMethod.setRequired(false);
        optExtOptions.addOption(optOldMethod);

        options.addOptionGroup(optExtOptions);

//        Class.forName("cn.edu.hit.kg.Neo4jDataProvider");

        HelpFormatter hf = new HelpFormatter();
        hf.setWidth(110);
        CommandLineParser parser = new DefaultParser();

        try {
            CommandLine commandLine = parser.parse(options, args);

            if (commandLine.hasOption('h')) {
                // 打印使用帮助
                hf.printHelp("帮助：", options, true);
                return;
            }

            int times=1;
            if (commandLine.hasOption('t')) {
                // 打印使用帮助
                String timesString = commandLine.getOptionValue('t');
                try {
                    times = Math.max(Integer.parseInt(timesString),1);
                } catch (NumberFormatException e) {
//                    e.printStackTrace();
                    System.out.println("执行次数应当为一个整数");
                }
            }

            String path = commandLine.getOptionValue('o');
            if (path==null || path.trim().length()==0){
                System.out.println("输出文件路径不可为空");
                return;
            }

            File baseFile = new File(path);
            if (!baseFile.exists()){
                baseFile.mkdirs();
            }

            String ipath = commandLine.getOptionValue('s');
            boolean useFeatureOnly = false;
            List<Signal> signals = new ArrayList<>();
            Map<Long,Boolean> conditions = new ConcurrentHashMap<>();
            if (ipath==null || ipath.trim().length()==0){
                System.out.println("输入路径不可为空");
                return;
            }else {
                if (commandLine.hasOption('x')){
                    useFeatureOnly = true;
                    try {
                        String signalJsonString = Files.lines(Paths.get(ipath)).collect(Collectors.joining());
                        JSONArray signalJArray = JSONArray.fromObject(signalJsonString);
                        for (int i = 0; i < signalJArray.size(); i++) {
                            JSONObject signalObj = signalJArray.getJSONObject(i);
                            Long id = signalObj.getLong("id");
                            boolean value = signalObj.getBoolean("value");
                            conditions.put(id,value);
                        }
                        System.out.println(conditions);
                    }catch (RuntimeException ex){
                        System.out.println("特征文件不规范，"+ex.getMessage());
                        return;
                    }
                }else {
                    try {
                        String signalJsonString = Files.lines(Paths.get(ipath)).collect(Collectors.joining());
                        JSONArray signalJArray = JSONArray.fromObject(signalJsonString);
                        for (int i = 0; i < signalJArray.size(); i++) {
                            JSONObject signalObj = signalJArray.getJSONObject(i);
                            Signal signal = new Signal();
                            signal.setComponent(Component.getByName(signalObj.getString("component")));
                            signal.setFeatureType(signalObj.getString("featureType"));
                            Object valueObj = signalObj.get("value");
                            signal.setValue(valueObj);
                            signals.add(signal);
                        }
                        System.out.println(signals);
                    }catch (RuntimeException ex){
                        System.out.println("特征文件不规范，"+ex.getMessage());
                        return;
                    }
                }
            }

            ConfigureLoader configureLoader = ConfigureLoader.getInstance();
            Document document = configureLoader.getBaseConfigure();
            GraphData graphData;
            if (commandLine.hasOption('n')){
                System.out.println("正在从Neo4j加载知识库，请稍候……");
                document.put("useNeo4j",true);
                document.put("NEO4J_BOLT",commandLine.getOptionValue('n'));
                document.put("USERNAME",commandLine.getOptionValue('u',"neo4j"));
                document.put("PASSWORD",commandLine.getOptionValue('p',"123456"));

//                GraphDataProvider provider = GraphDataProvider.getGraphDataProvider(document);

                graphData = GraphData.getInstance();
            }else {
                System.out.println("正在从文件加载知识库，请稍候……");
                document.put("useNeo4j",false);
                document.put("graphInClassPath",false);
                document.put("graphPath",commandLine.getOptionValue('f'));

                graphData = GraphData.getInstance();
            }
            GraphData.reloadData();
            graphData = GraphData.getInstance();

            System.out.println("开始诊断……");
            final String unitId="test";
            for (int i = 0; i < times; i++) {
                long timestamp = System.currentTimeMillis();

                System.out.println("graphData = " + graphData);
                List<Fault> faults =  graphData.getIdSetByLabel("Fault").stream()
                        .sequential()
                        .map(Fault::loadById)
                        .collect(Collectors.toList());

                List<Map> results = new ArrayList<>();
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("timestamp", timestamp);

                if (commandLine.hasOption('y')){
                    //使用旧策略
                    for (Fault fault : faults){
                        if (fault.matchSignal(signals,timestamp,unitId)){
                            results.add(fault.transferFaultToMap(unitId));
                        }
                    }
                }else {
                    //使用新策略
                    Map<Long,Boolean> conclusions = new ConcurrentHashMap<>();
                    if (useFeatureOnly){
                        conclusions = conditions;
                    }else {
                        for (Fault fault : faults) {
                            System.out.println(fault.getString("name")+"\t"+ fault.getId());
                            Set<Feature> features = fault.getFeatureSet();
                            for (Feature feature : features) {
                                boolean isMatched = feature.matchSignal(signals,timestamp,unitId);
                                System.out.println(feature.getString("name")+"\t"+feature.getId()+"\t"+feature.getString("opertion")+"\t"+isMatched);
                                conclusions.put(feature.getId(),isMatched);
                            }
                        }
                    }

                    for (Fault fault : faults) {
                        Map result = fault.matchByConclusionsAsReport(conclusions,unitId);
                        results.add(result);
                    }
                }

                jsonObject.put("data",results);

                Map nodes = new HashMap<>();
                if (results != null) {
                    results.stream()
                            .map(map -> (long) map.get("id"))
                            .map(BaseNode::loadNodeById)
                            .forEach(node -> nodes.put("" + node.getId(), node.toJson()));

                    results.stream()
                            .map(map -> (long) map.get("id"))
                            .flatMap(BaseNode::getAllSubNodes)
                            .map(BaseNode::loadNodeById)
                            .forEach(node -> nodes.put("" + node.getId(), node.toJson()));
                }
                jsonObject.put("node", nodes);
                System.out.println(jsonObject.toString());
                File outputFile = new File(baseFile,"result_"+timestamp+".json");
                PrintWriter printWriter = new PrintWriter(new FileWriter(outputFile));
                printWriter.println(jsonObject.toString());
                printWriter.flush();
                Thread.sleep(1000);
            }
        }catch (ParseException e) {
            hf.printHelp("参数错误！", options, true);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
